var path = require('path');
var FileOpe = require('./FileOpe'); // 文件操作
var Logger = require('./Logger'); // 日志操作
var HashOpe = require('./HashUtils'); // Hash 计算

var IPAddress = require('../wsp2p/IPAddress'); // ip地址（ protocal://ip:port）
var OnlineNodeList = require('../wsp2p/OnlineNodeList'); 
var WSConnectionList = require('../wsp2p/WSConnectionList');

/**
 * 工具类
 * 1. 配置文件方法、工具方法
 * 2. 文件操作对象、日志对象、Hash对象
 */
class AppUtils {
  constructor(configFilePath) {
    // 1.操作对象
    this.fileOpe = new FileOpe();
    this.logger = new Logger();
    this.HashOpe = HashOpe;
    this.birthTime = new Date().getTime();
    
    // 2.Web配置文件路径
    this.configFilePath = null;

    if (configFilePath && typeof configFilePath == 'string') {
      this.configFilePath = configFilePath;
    } else { 
      throw new Error('配置文件路径不能为空~');
    }

    // 2.1 当前程序的 配置数据{webIp,webPort,mineDiffcultLevel}
    this.webConfig = this.getWebAppConfigObj();
    this.updateWebSiteHttpPortInConfig();

    // 3.1 本机 express用的 web服务 uri
    this.webIpAddress = new IPAddress(this.webConfig.webIp, this.webConfig.webPort, 'http://');
    
    // 3.2 本机 webSocket 服务 uri，但初始化时，还不知道 ws 服务端 port
    this.wsIpAddress = new IPAddress(this.webConfig.webIp);

    // 4.1 在线节点列表:IPAddress
    this.onlineNodeList = new OnlineNodeList();
    // 4.2 与客户端连接通道 集合
    this.clientConnectionList = new WSConnectionList();

  }
  
  // --- 一、配置文件 ----------------------------------
  /**
   * 1.0 站点配置文件路径
   * @param {string} configFilePath 默认 ./WebConfig.json, path.join(__dirname, this.configFileRelativePath);
   */
  setConfigFilePath(configFilePath) {
    if (configFilePath && typeof configFilePath == 'string') {
      this.configFilePath = configFilePath;
    } else { 
      throw new Error('配置文件路径不能为空~');
    }
  }

  /**
   * 1.1 获取网站应用程序配置文件
   */
  getWebAppConfigObj() {
    var configObj = this.fileOpe.loadJsonObjFromDisk(this.configFilePath);

    // 如果配置文件空 或者 或者不存在，则使用默认值，并写入文件
    if (!configObj) { 
      configObj = { webIp: '127.0.0.1', webPort: 8101, mineDiffcultLevel: 3 };
      console.log(this.configFilePath);
      this.fileOpe.save2Disk(this.configFilePath, configObj);
    }
    return configObj;
  }

  /**
   * 1.2 获取网站的端口号，并自动把文件中保存的端口号 +1
   */
  updateWebSiteHttpPortInConfig() {
    if (!this.webConfig) {
      this.webConfig = this.getWebAppConfigObj();
    }
    // 给端口号 +1
    this.webConfig.webPort = this.webConfig.webPort + 1;
    // 存入硬盘
    this.fileOpe.save2Disk(this.configFilePath, this.webConfig);
    return this.webConfig.webPort;
  }

  /**
   * 1.3 从请求报文中 获取 浏览器端IP
   * @param req 由express获取的请求报文对象
   * @returns 浏览器ip地址
   */
  getClientIp(req) {
    var ipAddress;
    var forwardedIpsStr = req.header('x-forwarded-for');
    if (forwardedIpsStr) {
      var forwardedIps = forwardedIpsStr.split(',');
      ipAddress = forwardedIps[0];
    }
    if (!ipAddress) {
      ipAddress = req.connection.remoteAddress;
    }
    return ipAddress;
  }

  // --- 二、节点方法 ----------------------------------


  // --- 三、工具方法 ----------------------------------
  /**
   * 0.1 获取 AppUtils 单例对象
   * @param {string} webConfigPath 配置文件路径，第一次调用时需要传递
   */
  static getSingle(webConfigPath) {
    if (!this.onlyAppUtils) {
      this.onlyAppUtils = new AppUtils(webConfigPath);
    }
    return this.onlyAppUtils;
  }

  /**
   * 0.2 根据 json 获取 对象（jsons如果是字符串就转成对象）
   * @param jsons 需要转成对象的字符串 - 如果是对象会直接返回
   */
  getObj4JSON(jsons) { 
    if (typeof jsons == 'string') { 
      return JSON.parse(jsons);
    }
    return jsons;
  }

  /**
   * 0.3 判断是否为函数
   * @param func 
   */
  isFunc(func) { 
    return func && func instanceof Function;
  }
}

AppUtils.onlyAppUtils = null;

module.exports = AppUtils;



// //单例模式抽象，分离创建对象的函数和判断对象是否已经创建
// var getSingle = function (fn) {
//   var result;
//   return function () {
//       return result || ( result = fn.apply(this, arguments) );
//   }
// };